import time
import sys
import numpy as np
import subprocess
import os
import random

from pathlib import Path

from switchboard import PySbPacket, PySbTx, PySbRx, SbDut

THIS_DIR = Path(__file__).resolve().parent

def get_input_data() -> int:
    data = random.randint(0, 4294967295)
    return int(data)

def get_input_addr() -> int:
    addr = random.randint(0, 4294967295)
    return int(addr)

def send_to_axi(addr:int, data:int, tx:PySbTx, op:int): # op{0:read, 1:write}
    txp = PySbPacket(
        destination=0x3f3f3f3f,
        flags=1,
        data=np.arange(5, dtype=np.uint8)
    )
    txp.destination = addr
    txp.data[0] = (data >> 24) & 0x0000_00ff
    txp.data[1] = (data >> 16) & 0x0000_00ff
    txp.data[2] = (data >> 8) & 0x0000_00ff
    txp.data[3] = (data >> 0) & 0x0000_00ff
    txp.data[4] = op;
    print("SB: send txp to axi:")
    print(f"addr = {addr:x} data = {data:x} op = {op}")
    tx.send(txp)
    
def receive_from_axi(rx:PySbRx) -> tuple[int,int]:
    rxp = rx.recv()
    if not rxp:
        print("SB: No receive")
        return (0xffffffff,0xffffffff)
    else:
        tmp0 = int(rxp.data[0])
        tmp1 = int(rxp.data[1])
        tmp2 = int(rxp.data[2])
        tmp3 = int(rxp.data[3])
        data = (tmp0 << 24) | (tmp1 << 16) | (tmp2 << 8) | tmp3
        addr = rxp.destination
        valid = int(rxp.data[4])
        print("SB: receive rxp from axi:")
        print(f"addr = {addr:x} data = {data:x} valid = {valid}")
        return (addr,data,valid)
    
def main():
    subprocess.run(["xvlog","--sv","dpi_interface.sv"])
    subprocess.run(["xvlog","--sv","dpi_interface_test.sv"])
    subprocess.run(["xelab","-debug" ,"all" ,"--snapshot" ,"sp1" ,"dpi_interface_test"])
    xsim_process = subprocess.Popen(["xsim", "sp1"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    xsim_process.stdin.write("open_vcd\n")
    xsim_process.stdin.write("log_vcd *\n")
    xsim_process.stdin.write("add_wave *\n")
    xsim_process.stdin.write("run 20ns\n")
    
    tx = PySbTx('from_sb_to_axil.q', fresh=True)
    rx = PySbRx('from_axil_to_sb.q', fresh=True)
    
    # write
    
    input_data = 0xaabb0022
    input_addr = 0x80002000
    send_to_axi(input_addr, input_data, tx, 1)
    
    xsim_process.stdin.write("run 350ns\n")
    xsim_process.stdin.flush()
    
    output_addr,output_data,valid = receive_from_axi(rx)
    
    xsim_process.stdin.write("run 100ns\n")
    
    # read
    
    input_data = 0xaabb0022
    input_addr = 0x80002000
    send_to_axi(input_addr, input_data, tx, 0)
    
    xsim_process.stdin.write("run 350ns\n")
    xsim_process.stdin.flush()
    
    output_addr,output_data,valid = receive_from_axi(rx)
    
    xsim_process.stdin.write("save_wave_config all_signals.wcfg\n")
    xsim_process.stdin.write("close_vcd\n")
    xsim_process.stdin.write("quit\n")
    xsim_process.communicate() 
    
    print("xsim end")
    print("verilator end")
    
main()